const { get_full_path, remove } = require("./file_tool");
const transform_tool = require("./transform_tool");
const { warn, info, log, setLastError } = require("./log_tool");
const { progress } = require("./display_tool");
const fs = require("node:fs");
const { eval_code, shareData } = require("../commands/share_data");
const path = require("node:path");
const { hash } = require("./hash_tool");

async function doRequest(data) {
    const request = require("request");
    const request_progress = require("request-progress");

    const url = data.src || data?.target?.url;
    const urlObj = new URL(url);
    /** 是否启用 http 缓存 */
    const enable_http_cache = data.enable_cache || false;
    /** @type {http.RequestOptions} */
    let options = {
        method: "GET",
        rejectUnauthorized: false,
        timeout: 10 * 60 * 1000, // 超时(ms)
    };
    const maxRetryTimes = 0;
    let leftRetryTimes = maxRetryTimes;
    options = Object.assign(options, data.options);
    if (!options.proxy) {
        options.proxy = shareData.ENV.NODE_AUTOMATOR_PROXY;
    }
    /** @type {string} */
    let http_cache;
    const cache_ext = data.cache_ext || "";
    const cache_name = `[${options.method}]${hash(url)}.${path.basename(urlObj.pathname) || "index.html"}${cache_ext}`;
    const cache_path = path.resolve(
        shareData.AUTOMATOR_APPDATA,
        ".raw_cache",
        cache_name,
    );
    if (enable_http_cache && fs.existsSync(cache_path)) {
        http_cache = fs.readFileSync(cache_path);
    }
    let result;
    const useCache = enable_http_cache && http_cache;
    const quiet = !data.verbose;
    !quiet && info(`[${options.method}] ${url}${useCache ? "[缓存]" : ""}`);
    let hasProgress = false;
    const fileDst = get_full_path(
        data.dst || `${shareData.AUTOMATOR_SCRATCH}/request_tool/${cache_name}`,
        "FILE",
    );
    if (useCache) {
        result = http_cache;
        fs.writeFileSync(fileDst, http_cache);
    } else {
        const chunks = [];
        result = await new Promise((resolve) => {
            let sizeTotal = 0;
            const cacheDir = path.dirname(cache_path);
            if (!fs.existsSync(cacheDir)) {
                fs.mkdirSync(cacheDir, {
                    recursive: true,
                });
            }
            function send_request() {
                const req = request(url, options, (err, res, result) => {
                    if (err) {
                        // 检查重试
                        if (leftRetryTimes > 0) {
                            --leftRetryTimes;
                            !quiet && warn(`[重试] ${url} ${err}`);
                            send_request();
                            return;
                        }
                        setLastError(`${url} ${err}`);
                        resolve();
                        return;
                    }
                    const buffer = Buffer.concat(chunks);
                    sizeTotal = res.client.bytesRead;
                    !quiet &&
                        progress(sizeTotal, sizeTotal, {
                            desc: "完成",
                            format_bytes: true,
                            color: "green",
                        });
                    // 仅在响应码合法时，写入缓存
                    if (res.statusCode > 0 && res.statusCode < 400) {
                        if (enable_http_cache) {
                            fs.writeFileSync(cache_path, buffer);
                        }
                        fs.writeFileSync(fileDst, buffer);
                    }
                    if (!data.ignore_status_code) {
                        if (res.statusCode >= 400) {
                            setLastError(
                                `${options.method} ${url} ${res.statusCode} (${res.statusMessage}) "${typeof res.body === "string" ? res.body : JSON.stringify(res.body)}" `,
                            );
                        }
                    }
                    // 返回校验
                    if (data.response_validator) {
                        const [ok, errmsg] = eval_code(data.response_validator)(
                            res,
                        );
                        if (!ok) {
                            setLastError(errmsg || "校验失败");
                        }
                    }
                    switch (data.response_type) {
                        case "HEADERS": {
                            resolve(res.headers);
                            break;
                        }
                        case "BODY": {
                            resolve(res.body);
                            break;
                        }
                        case "STATUS_CODE": {
                            resolve(res.statusCode);
                            break;
                        }
                        case "RAW": {
                            resolve(res);
                            break;
                        }
                        default: {
                            if (options.method.toLowerCase() === "head") {
                                resolve(res.headers);
                            } else {
                                resolve(result);
                            }
                        }
                    }
                });
                req.on("data", (chunk) => {
                    chunks.push(chunk);
                });
                request_progress(
                    req.on("progress", (state) => {
                        sizeTotal = state.size.total;
                        if (!quiet) {
                            hasProgress = true;
                        }
                        !quiet &&
                            progress(
                                state.size.transferred,
                                state.size.total,
                                {
                                    desc: `请求中...(elapsed: ${transform_tool.formatTimeInSec(state.time.elapsed)}, speed: ${transform_tool.formatByteSize(state.speed)}/s, ETA: ${transform_tool.formatTimeInSec(state.time.remaining)})`,
                                    format_bytes: true,
                                    color: "cyan",
                                },
                                true,
                            );
                    }),
                );
            }
            send_request();
        });
    }
    if (hasProgress) {
        log("");
    }
    return result;
}

module.exports = {
    doRequest,
};
